﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace ESpace
{
    /// <summary>Математика</summary>
    public class EMath
    {
        /// <summary>sin в градусах</summary>
        static public double Sin(double x) { return Math.Sin((x / 180.0) * Math.PI); }
        /// <summary>cos в градусах</summary>
        static public double Cos(double x) { return Math.Cos((x / 180.0) * Math.PI); }
        /// <summary>tan в градусах</summary>
        static public double Tan(double x) { return Math.Tan((x / 180.0) * Math.PI); }
        /// <summary>arcSin в градусах</summary>
        static public double ASin(double x) { return Math.Asin(x) * 180.0 / Math.PI; }
        /// <summary>arcCos в градусах</summary>
        static public double ACos(double x) { return Math.Acos(x) * 180.0 / Math.PI; }
        /// <summary>arcTan в градусах</summary>
        static public double ATan(double x) { return Math.Atan(x) * 180.0 / Math.PI; }

        /// <summary>arcSin в градусах 360</summary>
        static public double ASin360(double x, bool right, bool up)
        {
            double result = Math.Asin(x) * 180.0 / Math.PI;
            if (right) return 180.0 - result;
            else if (up) return 360.0 + result;
            return result;
        }

        /// <summary>arcCos в градусах 360</summary>
        static public double ACos360(double x, bool up)
        {
            double result = Math.Acos(x) * 180.0 / Math.PI;
            if (up) return 360.0 - result;
            return result;
        }

        /// <summary>Гипотенуза</summary>
        static public double Hyp(double Katet1, double Katet2) { return Math.Sqrt(Math.Pow(Katet1, 2.0) + Math.Pow(Katet2, 2.0)); }
        /// <summary>Катет</summary>
        static public double Ktt(double Hypotenuse, double Katet) { return Math.Sqrt(Math.Pow(Hypotenuse, 2.0) - Math.Pow(Katet, 2.0)); }
        /// <summary>Противолежащий от гипотенузы угол</summary>
        static public double UgolProt(double hip, double protKat) { return ASin(protKat / hip); }
        /// <summary>Прилежащий к гипотенузе угол</summary>
        static public double UgolPril(double hip, double prilKat) { return ACos(prilKat / hip); }
       
        /// <summary>Прилежащий (ПРОВЕРИТЬ) к гипотенузе угол по катетам</summary>
        static public double UgolKtt(double prilKat, double protKat) { return ATan(protKat / prilKat); }

        #region *** проверено *************************************************

        /// <summary>Возвращает угол по двум точкам</summary>
        static public double GetAngle(Point p1, Point p2)
        {
            return GetAngle(p1.X, p1.Y, p2.X, p2.Y);
        }

        static public double GetAngle(double x1, double y1, double x2, double y2)
        {
            if (y2 == y1) { if (x2 >= x1) return 0; else return 180; }
            if (x2 == x1) { if (y2 >= y1) return 90; else return 270; }

            double ret = ATan((y2 - y1) / (x2 - x1));
            if (ret < 0) ret += 180;
            if (y2 < y1) ret += 180;
            return ret;

            /* (проигрываем по времени)
             if (p1 == p2) return 0;
            double x = p2.X - p1.X;
            double y = p2.Y - p1.Y;
            return ACos360(x / Hyp(x, y), (y < 0));
             */
        }
       
        // расстояние между двумя точками
        static public double GetLen(Point p1, Point p2)
        {
            return Hyp(p1.X - p2.X, p1.Y - p2.Y);
        }

        // координата второйточки по расстоянию и направлению
        static public Point GetPoint(Point p1, double Len, double angle)
        {
            Point p = new Point();
            p.X = (int)(p1.X + Len * Cos(angle));
            p.Y = (int)(p1.Y + Len * Sin(angle));
            return p;
        }

        #region *** треугольник ***
        public struct aT // стороны треугольника
        {
            public double a; //угол точки A
            public double b; //угол точки B
            public double c; //угол точки C
        }

        /// <summary>Возвращает углы треугольника по трем точкам</summary>
        public static aT AnglesTriangle(Point A, Point B, Point C)
        {
            double x;
            double y;
            // стороны
            x = Math.Abs(B.X - C.X);
            y = Math.Abs(B.Y - C.Y);
            double a = EMath.Hyp(x, y);

            x = Math.Abs(A.X - C.X);
            y = Math.Abs(A.Y - C.Y);
            double b = EMath.Hyp(x, y);

            x = Math.Abs(A.X - B.X);
            y = Math.Abs(A.Y - B.Y);
            double c = EMath.Hyp(x, y);

            aT res = new aT();

            res.a = AnglesTriangleA(a, b, c);
            res.b = AnglesTriangleA(b, a, c);
            res.c = AnglesTriangleA(c, a, b);

            return res;
        }

        /// <summary>Нахождение угла напротив стороны 'а' по трем сторонам (для внутренних вычислений)</summary>
        static double AnglesTriangleA(double a, double b, double c)
        {
            return EMath.ACos((b * b + c * c - a * a) / (2.0 * b * c));
        }

        /// <summary>Нахождение угла в точке A по трем точкам (для внутренних вычислений)</summary>
        static double AnglesTriangleA(Point A, Point B, Point C)
        {
            double x;
            double y;
            // стороны
            x = Math.Abs(B.X - C.X);
            y = Math.Abs(B.Y - C.Y);
            double a = EMath.Hyp(x, y);

            x = Math.Abs(A.X - C.X);
            y = Math.Abs(A.Y - C.Y);
            double b = EMath.Hyp(x, y);

            x = Math.Abs(A.X - B.X);
            y = Math.Abs(A.Y - B.Y);
            double c = EMath.Hyp(x, y);

            return AnglesTriangleA(a, b, c);
        }

        #endregion *** треугольник ***

        #endregion *** проверено *************************************************

        /// <summary> Относительные координаты точки </summary>
        /// <param name="ZeroPoint">точка, принятая за 0</param>
        /// <param name="shift">линейное смещение от этой точки по X Y</param>
        /// <param name="angle">повторот мира вокруг точки ZeroPoint</param>
        /// <returns></returns>
        static public Point OtnCoord(Point ZeroPoint, Point shift, double angle)
        {
            Point p = new Point();

            //*** оптимизация **************************
            if (angle == 0)
            {
                p.X = ZeroPoint.X + shift.X;
                p.Y = ZeroPoint.Y + shift.Y;
                return p;
            }
            if (angle == 90)
            {
                p.X = ZeroPoint.X - shift.Y;
                p.Y = ZeroPoint.Y + shift.X;
                return p;
            }
            if (angle == 180)
            {
                p.X = ZeroPoint.X - shift.X;
                p.Y = ZeroPoint.Y - shift.Y;
                return p;
            }
            if (angle == 270)
            {
                p.X = ZeroPoint.X + shift.Y;
                p.Y = ZeroPoint.Y - shift.X;
                return p;
            }
            //end*** оптимизация ***********************

            // длина
            double l = Hyp(shift.X, shift.Y);
            // угол направления
            double a = angle;
            if (shift.Y > 0) a += ACos(shift.X / l);
            if (shift.Y < 0) a += ACos360(shift.X / l, true);
            p.X = (int)(ZeroPoint.X + l * Cos(a));
            p.Y = (int)(ZeroPoint.Y + l * Sin(a));
            return p;
        }
        //---------------------------------------------------------------------------

        #region *** EMathF ***************************************

        /// <summary> точка в прямоугольнике </summary>
        public static bool PointInRect(Rectangle r, Point p)
        {
            if (p.Y >= r.Top & p.Y < r.Bottom & p.X >= r.Left & p.X < r.Right)
                return true;
            if (p.Y < r.Top & p.Y >= r.Bottom & p.X < r.Left - 1 & p.X >= r.Right)
                return true;
            return false;
        }
        
        /// <summary> отрезок пересекает прямоугольник </summary>
        public static bool LineCrossRect(Rectangle r, Point p1, Point p2)
        {
            // если одна из точек принадлежит прямоугольнику, отрезок в прямоугольнике
            if (PointInRect(r, p1)) return true;
            if (PointInRect(r, p2)) return true;

            // смотрим, пересекает ли отрезок какую, либо диагональ прямоугольника
            Point p3, p4;
            p3 = new Point(r.X, r.Y);
            p4 = new Point(r.Right, r.Bottom);
            if (CrossingLines(p1, p2, p3, p4)) return true;
            p3.X = r.Right;
            p3.Y = r.Y;
            p4.X = r.X;
            p4.Y = r.Bottom;

            if (CrossingLines(p1, p2, p3, p4)) return true;

            return false;
        }

        public static bool CrossRect(Rectangle r1, Rectangle r2)
        {
            if (PointInRect(r1, new Point(r2.Left, r2.Top))) return true;
            if (PointInRect(r1, new Point(r2.Right, r2.Top))) return true;
            if (PointInRect(r1, new Point(r2.Left, r2.Bottom))) return true;
            if (PointInRect(r1, new Point(r2.Right, r2.Bottom))) return true;

            if (LineCrossRect(r1, new Point(r2.Left, r2.Top), new Point(r2.Right, r2.Top))) return true;
            if (LineCrossRect(r1, new Point(r2.Right, r2.Top), new Point(r2.Right, r2.Bottom))) return true;
            if (LineCrossRect(r1, new Point(r2.Right, r2.Bottom), new Point(r2.Left, r2.Bottom))) return true;
            if (LineCrossRect(r1, new Point(r2.Left, r2.Bottom), new Point(r2.Left, r2.Top))) return true;

            if (PointInRect(r2, new Point(r1.Left, r1.Top))) return true;

            return false;
        }

        #region пересечение линий

        public static bool CrossingLines(Rectangle L1, Rectangle L2)
        {
            if (EqalLine(L1, L2)) return true;

            Point a, b, c, d;
            a = new Point(L1.X, L1.Y);
            b = new Point(L1.Right, L1.Bottom);
            c = new Point(L2.X, L2.Y);
            d = new Point(L2.Right, L2.Bottom);

            double common = (b.X - a.X) * (d.Y - c.Y) - (b.Y - a.Y) * (d.X - c.X);

            if (common == 0)
                return false;

            double rH = (a.Y - c.Y) * (d.X - c.X) - (a.X - c.X) * (d.Y - c.Y);
            double sH = (a.Y - c.Y) * (b.X - a.X) - (a.X - c.X) * (b.Y - a.Y);

            double r = rH / common;
            double s = sH / common;

            if (r >= 0 && r <= 1 && s >= 0 && s <= 1)
                return true;
            else
                return false;
        }
        //---------------------------------------------------------------------------
        public static bool CrossingLines(Point p1, Point p2, Point p3, Point p4)
        {
            if (EqalLine(p1, p2, p3, p4)) return true;

            double common = (p2.X - p1.X) * (p4.Y - p3.Y) - (p2.Y - p1.Y) * (p4.X - p3.X);

            if (common == 0)
                return false;

            double rH = (p1.Y - p3.Y) * (p4.X - p3.X) - (p1.X - p3.X) * (p4.Y - p3.Y);
            double sH = (p1.Y - p3.Y) * (p2.X - p1.X) - (p1.X - p3.X) * (p2.Y - p1.Y);

            double r = rH / common;
            double s = sH / common;

            if (r >= 0 && r <= 1 && s >= 0 && s <= 1)
                return true;
            else
                return false;
        }
        //---------------------------------------------------------------------------
        public static bool CrossingLines1(Rectangle L1, Rectangle L2)
        {
            if (EqalLine(L1, L2)) return true;
            double x1, y1, x2, y2; // первый отрезок
            double x3, y3, x4, y4; // второй отрезок

            x1 = L1.X;
            x2 = L1.Right;
            y1 = L1.Y;
            y2 = L1.Bottom;
            x3 = L2.X;
            x4 = L2.Right;
            y3 = L2.Y;
            y4 = L2.Bottom;

            double A1 = y2 - y1;
            double B1 = x1 - x2;
            double C1 = -A1 * x1 - B1 * y1;

            double A2 = y4 - y3;
            double B2 = x3 - x4;
            double C2 = -A2 * x3 - B2 * y3;

            double f1 = A1 * x3 + B1 * y3 + C1;
            double f2 = A1 * x4 + B1 * y4 + C1;
            double f3 = A2 * x1 + B2 * y1 + C2;
            double f4 = A2 * x2 + B2 * y2 + C2;

            // если лежат на одной прямой
            if (f1 == 0 && f2 == 0 && f3 == 0 && f4 == 0)
            {
                double l1 = Math.Sqrt(sqr(x1 - x2) + sqr(y1 - y2));
                double l2 = Math.Sqrt(sqr(x3 - x4) + sqr(y3 - y4));
                return sqr((x1 + x2) - (x3 + x4)) + sqr((y1 + y2) - (y3 + y4)) < sqr(l1 + l2);
            }
            // строгое пересечение
            //return  (f1 * f2 < 0 && f3 * f4 < 0);
            //или
            // с учетом касания (так на самом деле нельзя, нужно сравнивать с учетом погрешности, но я так написал для ясности)
            return (f1 * f2 <= 0 && f3 * f4 <= 0);

        }
        //---------------------------------------------------------------------------
        public static bool CrossingLines1(Point p1, Point p2, Point p3, Point p4)
        {
            if (EqalLine(p1, p2, p3, p4)) return true;
            double x1, y1, x2, y2; // первый отрезок
            double x3, y3, x4, y4; // второй отрезок

            x1 = p1.X;
            x2 = p2.X;
            y1 = p1.Y;
            y2 = p2.Y;
            x3 = p3.X;
            x4 = p4.X;
            y3 = p3.Y;
            y4 = p4.Y;

            double A1 = y2 - y1;
            double B1 = x1 - x2;
            double C1 = -A1 * x1 - B1 * y1;

            double A2 = y4 - y3;
            double B2 = x3 - x4;
            double C2 = -A2 * x3 - B2 * y3;

            double f1 = A1 * x3 + B1 * y3 + C1;
            double f2 = A1 * x4 + B1 * y4 + C1;
            double f3 = A2 * x1 + B2 * y1 + C2;
            double f4 = A2 * x2 + B2 * y2 + C2;

            // если лежат на одной прямой
            if (f1 == 0 && f2 == 0 && f3 == 0 && f4 == 0)
            {
                double l1 = Math.Sqrt(sqr(x1 - x2) + sqr(y1 - y2));
                double l2 = Math.Sqrt(sqr(x3 - x4) + sqr(y3 - y4));
                return sqr((x1 + x2) - (x3 + x4)) + sqr((y1 + y2) - (y3 + y4)) < sqr(l1 + l2);
            }
            // строгое пересечение
            //return  (f1 * f2 < 0 && f3 * f4 < 0);
            //или
            // с учетом касания (так на самом деле нельзя, нужно сравнивать с учетом погрешности, но я так написал для ясности)
            return (f1 * f2 <= 0 && f3 * f4 <= 0);

        }
        //---------------------------------------------------------------------------
        static double sqr(double d)
        {
            if (d < 0) d *= -1;
            if (d == 0) return 0.0;
            else return Math.Sqrt(d);
        }
        //---------------------------------------------------------------------------
        static bool EqalLine(Rectangle Line1, Rectangle Line2)
        {
            return (PointInLine(new Point(Line1.X, Line1.Y), Line2) ||
                    PointInLine(new Point(Line1.Right, Line1.Bottom), Line2) ||
                    PointInLine(new Point(Line2.X, Line2.Y), Line1) ||
                    PointInLine(new Point(Line2.Right, Line2.Bottom), Line1));
        }
        static bool EqalLine(Point p1, Point p2, Point p3, Point p4)
        {
            return (PointInLine(p1, p3, p4) ||
                    PointInLine(p2, p3, p4) ||
                    PointInLine(p3, p1, p2) ||
                    PointInLine(p4, p1, p2));
        }
        //---------------------------------------------------------------------------
        static bool PointInLine(Point p, Rectangle Ln)
        {
            if (
                 (
                    (
                       (Ln.Y <= p.Y) &&
                       (Ln.Bottom > p.Y)
                    ) ||
                    (
                       (Ln.Bottom <= p.Y) &&
                       (Ln.Y > p.Y)
                    )
                 ) &&
                 (
                    p.X == (Ln.Right - Ln.X) * (p.Y - Ln.Y) / (Ln.Bottom - Ln.Y) + Ln.X)
              )
            {
                return true;
            }
            return false;
        }
        static bool PointInLine(Point p, Point p1, Point p2)
        {
            if (
                 (
                    (
                       (p1.Y <= p.Y) &&
                       (p2.Y > p.Y)
                    ) ||
                    (
                       (p2.Y <= p.Y) &&
                       (p1.Y > p.Y)
                    )
                 ) &&
                 (
                    p.X == (p2.X - p1.X) * (p.Y - p1.Y) / (p2.Y - p1.Y) + p1.X)
              )
            {
                return true;
            }
            return false;
        }

        # endregion пересечение линий

        // точка в полигоне
        public static bool PointInPoly(Point[] pts, Point p)
        {
            int i, j;
            bool c = false;
            int n = pts.Length;
            for (i = 0, j = n - 1; i < n; j = i++)
            {
                // если найдено пересечение
                if (
                     (
                        (
                           (pts[i].Y <= p.Y) &&
                           (pts[j].Y > p.Y)
                        ) ||
                        (
                           (pts[j].Y <= p.Y) &&
                           (pts[i].Y > p.Y)
                        )
                     ) &&
                     (
                        p.X < (pts[j].X - pts[i].X) * (p.Y - pts[i].Y) / (pts[j].Y - pts[i].Y) + pts[i].X)
                  )
                {
                    c = !c; // переворачиваем результат каждый раз, когда находим пересечение
                }
            }

            return c;

        }

        //---------------------------------------------------------------------------
        public static bool PolyCrossRect1(Point[] p, Rectangle r)
        {
            if (PointInPoly(p, new Point(r.X, r.Y))) return true;
            if (PointInPoly(p, new Point(r.Right, r.Y))) return true;
            if (PointInPoly(p, new Point(r.Right, r.Bottom))) return true;
            if (PointInPoly(p, new Point(r.X, r.Bottom))) return true;

            int n = p.Length;
            int j = n - 1; // всегда на 1 меньше i. Причем первый элемент = последнему элементу массива
            for (int i = 0; i < n; i++)
            {
                if (LineCrossRect(r, p[j], p[i])) return true;
                j = i;
            }

            return false;
        }

        // прямоугольник или его часть находятся в многоугольнике, 
        // если хотябы один угол прямоугольника находится в многоугольнике
        // решение будет верным, только при условии, 
        // что нигде толщина многоугольника не становиться меньше квадрата 
        public static bool PolyCrossRect(Point[] p, Rectangle r)
        {
            if (PointInPoly(p, new Point(r.X, r.Y))) return true;
            if (PointInPoly(p, new Point(r.Right, r.Y))) return true;
            if (PointInPoly(p, new Point(r.Right, r.Bottom))) return true;
            if (PointInPoly(p, new Point(r.X, r.Bottom))) return true;

            return false;
        }

        #endregion *** EMathF ***************************************

    }
}
